home *** CD-ROM | disk | FTP | other *** search
/ The X-Philes (2nd Revision) / The X-Philes Number 1 (1995).iso / xphiles / hp48hor2 / mlinput.doc < prev    next >
Text File  |  1995-03-31  |  41KB  |  933 lines

  1.  
  2.  
  3.                         HP48SX Keyboard Input 
  4.  
  5.  
  6.              A Guide for the Machine Language Programmer 
  7.  
  8.                                   by 
  9.  
  10.                               Joe Ervin 
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19. 1  INTRODUCTION 
  20.  
  21. In this document, we will examine the workings of the HP48 keyboard 
  22. hardware and how to do your own keyboard input on the HP48 from 
  23. machine language.  Additionally, we will describe how the HP48 
  24. keyboard input scheme works during normal operation, and how you can 
  25. disable the normal operation and take over direct control of the 
  26. keyboard. 
  27.  
  28.  
  29.  
  30.  
  31. 2  OVERVIEW OF HP48 KEYBOARD INPUT 
  32.  
  33. During normal operation, the CPU scans the keyboard for key presses 
  34. every 1ms.  This 1ms keyboard scan is performed directly by the CPU 
  35. hardware with no involvement from software, and therefore has a 
  36. negligible effect on CPU performance.  During this automatic keyboard 
  37. scan, the entire keyboard is scanned in a single operation.  If no 
  38. keys are currently being pressed, then nothing else happens and the 
  39. CPU continues executing instructions normally.  If, however, this 
  40. automatic keyboard scan indicates that one or more keys are being 
  41. pressed, then the hardware interrupts the CPU.  The HP48's interrupt 
  42. handler then determines the exact key or keys which are being pressed 
  43. and updates the keyboard data structures in memory to reflect the new 
  44. state of the keyboard.  The keyboard data structures are described 
  45. later in this document. 
  46.  
  47. In addition to determining which specific keys are pressed, the HP48's 
  48. interrupt handler also scans all other possible sources of interrupts 
  49. and services them appropriately.  In the case of pressed keys, the 
  50. interrupt handler additionally schedules a timer interrupt for 1/16 of 
  51. a second into the future before returning to the interrupted program. 
  52. When the timer expires, another interrupt is generated, and the whole 
  53. operation repeats.  As a result, the CPU is interrupted 16 times per 
  54. second, for as long as a key is held down. 
  55.  
  56. The use of the timer interrupt is necessary because the keyboard 
  57. interrupt hardware only generates interrupts when keys are pressed, 
  58.  
  59.  
  60.                                                                 Page 2 
  61.  
  62.  
  63. i.e.  no interrupts are generated directly as a result of keys being 
  64. released.  Therefore, by continually scheduling the timer interrupt to 
  65. retrigger an interrupt in the near future, the interrupt handler can 
  66. effectively "poll" the keyboard 16 times per second, checking for keys 
  67. being released.  This is done so that the "key-released" event can be 
  68. recorded in the keyboard data structures, described below.  Machine 
  69. language applications can then check these data structures to 
  70. determine the exact state of each key on the keyboard. 
  71.  
  72.  
  73.  
  74. 3  SCANNING THE KEYBOARD FROM MACHINE LANGUAGE 
  75.  
  76. Unlike the automatic 1ms keyboard scan performed by the CPU's 
  77. hardware, the interrupt handler must force keyboard scans from 
  78. software to determine the exact state of the keyboard.  This is 
  79. necessary for the interrupt handler because any interrupts generated 
  80. by the 1ms keyboard scan indicate only that at least one key has been 
  81. pressed.  The interrupt handler then must scan the keyboard row by row 
  82. to determine exactly what keys are being pressed.  This same approach 
  83. can be used from machine language programs to scan the keyboard. 
  84.  
  85. Scanning the keyboard from software happens in two general phases; an 
  86. output phase and an input phase.  During the output phase, software 
  87. causes the HP48 to output signals to the keyboard which indicate the 
  88. exact row or rows which are being scanned.  Then, during the input 
  89. phase, the software reads the keyboard to see which of the scanned 
  90. rows have keys pressed. 
  91.  
  92. The keyboard is wired in a matrix as shown in Figure 1. 
  93.  
  94.  
  95.                                                                 Page 3 
  96.  
  97.  
  98.  
  99.  
  100.                 IN  #20 #10 #08 #04 #02 #01 
  101.  
  102.  
  103.          OUT   (bit)  5   4   3   2   1   0 
  104.  
  105.         #100    8         B   C   D   E   F 
  106.  
  107.         #080    7       PRG CST VAR  up NXT 
  108.  
  109.         #040    6       STO EVL <<< dwn >>> 
  110.  
  111.         #020    5       COS TAN sqt pwr inv 
  112.  
  113.         #010    4   ON* ENT +/- EEX DEL <== 
  114.  
  115.         #008    3   alp SIN  7   8   9   / 
  116.  
  117.         #004    2   yel MTH  4   5   6   x 
  118.  
  119.         #002    1   blu  A   1   2   3   - 
  120.  
  121.         #001    0        '   0   .  SPC  + 
  122.  
  123.  
  124.    (*) The ON key is actually in a column of its own. 
  125.    The ON key is represented in bit 15 of the data returned 
  126.    from an IN.4 instruction.  #xxx refers to the keyboard scan  
  127.    bits driven with the OUT instruction.    
  128.  
  129.                                Figure 1 
  130.  
  131.  
  132.  
  133.  
  134.  
  135. 3.1  The OUT Instruction. 
  136.  
  137. During the first phase of the keyboard scan, software executes the 
  138. OUT.X C instruction, which loads the HP48's output register with the 
  139. contents of C.x, and then drives those contents into the key matrix. 
  140. The bits in OUT<8:0> are connected to keyboard ROWS <8:0> 
  141. respectively.  Each "1" bit in OUT<8:0> therefore drives a HIGH 
  142. voltage level out to the corresponding keyboard row.  If any key in 
  143. that row is currently pressed, then the HIGH level propagates to the 
  144. bit of the input register which corresponds to the COLUMN of the key 
  145. being pressed.  Normally a HIGH voltage level on the input port causes 
  146. an interrupt, but we will show later how to disable this interrupt so 
  147. that the machine language program will be unhindered by the HP48's 
  148. interrupt handler. 
  149.  
  150.  
  151.  
  152.                                                                 Page 4 
  153.  
  154.  
  155. 3.2  The IN.4 Instruction 
  156.  
  157. During the second phase of the keyboard scan, software executes the 
  158. "IN.4 C" instruction to see if any keys in the scanned rows were 
  159. pressed.  In this way, a machine language program can scan the 
  160. keyboard to test whether specific keys are being pressed. 
  161.  
  162. With the exception of the ON key, the keys which are "visible" to the 
  163. input port is a function of the bit pattern driven during the OUT 
  164. instruction.  For example, if the output register contains 1FFh, then 
  165. all keyboard rows are visible to the IN register.  If, say, the [<-] 
  166. key is pressed (or any other key or keys in that column), then after 
  167. the "IN.4 C" instruction executes, bit 0 of the C register will be 
  168. set, indicating that at least one key in the rightmost column of the 
  169. scanned rows was pressed.  Note that if more than one keyboard row is 
  170. being scanned, then the data in IN<5:0> does not indicate a specific 
  171. key, but rather a specific column of any of the scanned keyboard rows, 
  172. as depicted by Figure 1.  The state of the ON key is always reflected 
  173. in bit 15 of the data returned from the IN.4 instruction. 
  174.  
  175. Because of the row/column wiring of the HP48 keyboard, in order to 
  176. determine exactly which keys are pressed it is necessary to perform 
  177. multiple OUT/IN pairs in which each "OUT.x C" scans only a single 
  178. keyboard row. 
  179.  
  180. For example, "OUT.x C" with C.x=001h scans only the bottom row of the 
  181. keyboard; C=002h scans the second row from the bottom; C=004h scans 
  182. the third row from the bottom, and so on.  In this way, any set bits 
  183. in the data returned by the IN.4 instruction indicate that a specific 
  184. key is being pressed.  This is what the HP48's interrupt handler does 
  185. to determine the exact status of the keyboard after the 1ms keyboard 
  186. scan has generated an interrupt due to one or more keys being pressed. 
  187. During normal operation, the interrupt handler then updates the 
  188. "KeyBuf" and the "KeyState" in memory (described below) to indicate 
  189. the key status to the rest of the RPL system. 
  190.  
  191.  
  192.  
  193. 3.3  Interrupt Woes 
  194.  
  195. In the process of servicing other I/O devices, checking battery 
  196. voltage, etc., the interrupt handler executes many hundreds of 
  197. instructions which have nothing to do with the keyboard, making the 
  198. handling of keyboard input through this mechanism very inefficient in 
  199. terms of CPU utilization.  More significantly, however, the interrupt 
  200. handler debounces the keyboard by capturing the state of the entire 
  201. keyboard repeatedly, waiting 2ms between samples, until it sees 
  202. exactly the same keyboard state for 5 consecutive samples.  Because of 
  203. this, the interrupt handler is guaranteed to require over 10ms to 
  204. detect a single keystroke. 
  205.  
  206. Furthermore, the keyboard service routine in the interrupt handler is 
  207. implemented as a loop which synchronizes itself to the 16Hz timer 
  208. (TIMER1).  If the keyboard service routine detects any keys being held 
  209. down, then after updating the keyboard data structures (see below) it 
  210.  
  211.  
  212.                                                                 Page 5 
  213.  
  214.  
  215. checks to see whether there is enough time before the next 16Hz tick 
  216. to do another full pass through the keyboard service routine and if 
  217. so, the code loops back to the top of the keyboard service routine. 
  218. If the next 16Hz clock tick is less than approximately 17ms into the 
  219. future, then the keyboard service routine exits, and the interrupt 
  220. handler completes.  However, since the interrupt handler scheduled 
  221. TIMER1 to interrupt on the next 16Hz tick, the CPU will bounce back 
  222. into the interrupt service routine in just a few milliseconds.  Thus, 
  223. holding down a key while the HP48's normal keyboard interrupt service 
  224. is in operation causes the keyboard interrupt service routine to hog 
  225. approximately 75% of the CPU, leaving the remaining 25% for your 
  226. application. 
  227.  
  228. When writing machine language programs that do not require much CPU 
  229. power, this may not be a concern and it may be desirable to allow the 
  230. interrupt system to handle keyboard input normally.  In this case, the 
  231. machine language program can retrieve keyboard status from the two 
  232. keyboard data structures described below.  However, for CPU-intensive 
  233. applications it is generally desirable to disable the HP48's normal 
  234. keyboard interrupts and perform keyboard I/O directly in the 
  235. application. 
  236.  
  237.  
  238.  
  239. 3.4  HP48 Keyboard Data Structures 
  240.  
  241. There are two main keyboard-related data structures that the HP48 
  242. keeps in memory.  The first is called the "KeyBuf", and occupies 34 
  243. nibbles starting at #704EA.  The second is called the "KeyState" and 
  244. occupies 13 nibbles starting at #704DD.  Additionally, there are three 
  245. other datum kept in memory which the interrupt handler uses in 
  246. conjunction with the keyboard.  These are the "ORshadow" (Output 
  247. Register shadow), which occupies 3 nibbles at #704C3, "KBdisable", 
  248. which is a single nibble at #704DC, and a two nibble value at #706C3 
  249. representing the state of the display annunciator flags at 10B/10C. 
  250. These data structures are described below. 
  251.  
  252.  
  253.  
  254. 3.4.1  The KeyBuf 
  255.  
  256. The first two nibbles of the KeyBuf are the "get" and "put" pointers, 
  257. respectively, and the remaining 32 nibbles comprise a 16-entry key 
  258. buffer, with each entry occupying one byte.  The "get" pointer 
  259. provides an index into the KeyBuf for the next available key code. 
  260. Similarly, the "put" pointer provides a index to the next entry in the 
  261. KeyBuf to be written.  If the "get" and "put" pointers are equal, then 
  262. the buffer is empty.  The "get" and "put" pointers point to byte 
  263. locations within the buffer. 
  264.  
  265. You may have noticed how your calculator beeps at you defiantly when 
  266. you have exceeded this 16 entry type-ahead buffer.  This is the 
  267. interrupt handler telling you that there is no more room in the 
  268. KeyBuf. 
  269.  
  270.  
  271.                                                                 Page 6 
  272.  
  273.  
  274. The key codes which are used to represent keys in the KeyBuf are 
  275. different than the scan patterns read in during the OUT/IN procedure. 
  276. Each key is given a unique 1-byte key code as follows:  key codes are 
  277. numbered sequentially from the upper left of the keyboard counting 
  278. across and down.  Thus a key code of 1 represents the [A] key, 2 for 
  279. [B], 3 for [C]..., #19h for [ENTER],...  #1Fh for [7],...  #31h for 
  280. [+].  Note that four keys do not obey this ordering:  [alpha] is #80h, 
  281. [leftshift] is #40h, and [rightshift] is #C0h, and [ON] has no key 
  282. code.  The ON key is handled as a special case by the interrupt 
  283. handler and does not appear in KeyState or in the KeyBuf. 
  284.  
  285. If the keyboard service routine detects the presence of [alpha], 
  286. [leftshift], or [rightshift] at the same time as another key, then the 
  287. key code for the "shift" key is ORed into the keycode for any 
  288. non-"shift" keys which may also be pressed, and the resulting keycodes 
  289. are inserted into the KeyBuf.  For example, the "right-shifted" 
  290. keycode for the [A] key is [rightshift]![A] = C0h!01h = C1h. 
  291. Similarly, the keycode for a "alpha-shifted" [ENTER] is 
  292. [alpha]![ENTER] = 80h!19h = 99h.  If one of the shift keys is detected 
  293. alone, then its keycode is simply inserted into the KeyBuf. 
  294.  
  295. The code example in Appendix A shows a simple way to remove keys from 
  296. the key buffer.  In addition, this code example shows how to put the 
  297. calculator into "light sleep" awaiting a key press. 
  298.  
  299.  
  300.  
  301. 3.4.2  The KeyState 
  302.  
  303. The 13 nibbles at location #704DD provide a bit pattern which reflects 
  304. the status of the keyboard, with each bit representing the state of a 
  305. specific key.  This bit pattern is updated whenever a key is pressed, 
  306. and is also updated when keys are released via a timer interrupt as 
  307. described above.  When a key is pressed, its corresponding bit is set 
  308. to 1.  There are 13 nibbles, 4 bits each, making 52 bits.  The [ON] 
  309. key is not represented in KeyState.  There are 48 keys remaining, so 
  310. four bits are unused.  The low bit is unused, the next bit corresponds 
  311. to the bottom rightmost key [+], the one following corresponds to SPC, 
  312. then comes period [.], [0], ['], after which immediately follows the 
  313. next row:  [-], [3], and so forth, up to the upper leftmost key, which 
  314. is [B].  (See Figure 1 above for the physical layout of the keyboard). 
  315. Below is a table of all key bit codes.  More than one bit is set if 
  316. more than one key is being held down simultaneously. 
  317.  
  318.  
  319.                                                                 Page 7 
  320.  
  321.  
  322.  
  323.   Key    704E7 704E2 704DD            Key    704E7 704E2 704DD  
  324.  
  325.    B    |  1  |     |     |           <==   |     |   1 |     | 
  326.    C    |   8 |     |     |           alp   |     |    8|     | 
  327.    D    |   4 |     |     |           SIN   |     |    4|     | 
  328.    E    |   2 |     |     |            7    |     |    2|     | 
  329.    F    |   1 |     |     |            8    |     |    1|     | 
  330.   PRG   |    8|     |     |            9    |     |     |8    | 
  331.   CST   |    4|     |     |            /    |     |     |4    | 
  332.   VAR   |    2|     |     |           yel   |     |     |2    | 
  333.    up   |    1|     |     |           MTH   |     |     |1    | 
  334.   NXT   |     |8    |     |            4    |     |     | 8   | 
  335.   STO   |     |4    |     |            5    |     |     | 4   | 
  336.   EVL   |     |2    |     |            6    |     |     | 2   | 
  337.   <<<   |     |1    |     |            x    |     |     | 1   | 
  338.   dwn   |     | 8   |     |           blu   |     |     |  8  | 
  339.   >>>   |     | 4   |     |            A    |     |     |  4  | 
  340.   COS   |     | 2   |     |            1    |     |     |  2  | 
  341.   TAN   |     | 1   |     |            2    |     |     |  1  | 
  342.   sqt   |     |  8  |     |            3    |     |     |   8 | 
  343.   pwr   |     |  4  |     |            -    |     |     |   4 | 
  344.   inv   |     |  2  |     |            '    |     |     |   2 | 
  345.   ENT   |     |  1  |     |            0    |     |     |   1 | 
  346.   +/-   |     |   8 |     |            .    |     |     |    8| 
  347.   EEX   |     |   4 |     |           SPC   |     |     |    4| 
  348.   DEL   |     |   2 |     |            +    |     |     |    2| 
  349.  
  350.  
  351.  
  352.                           Figure 2  KeyState 
  353.  
  354.  
  355.  
  356. 3.4.3  KBdisable And ORshadow 
  357.  
  358. As with any computer system that utilizes interrupts, the servicing of 
  359. interrupts must be transparent to the currently running application, 
  360. aside from the time delay associated with servicing the interrupt.  In 
  361. order to do this, the entire state of the CPU is saved at the 
  362. beginning of the interrupt handler, and restored again at the end. 
  363. Unfortunately, the contents of the OUT register is not readable, so it 
  364. is not possible to directly save its contents.  Since the interrupt 
  365. handler invariably modifies the contents of the OUT register, the HP48 
  366. interrupt system requires applications to maintain a copy of the OUT 
  367. register in RAM.  The 3 nibbles stored at #704C3, designated as the 
  368. "ORshadow", is used for this purpose. 
  369.  
  370. The ORshadow is used by applications to shadow the contents of the OUT 
  371. register.  It is the responsibility of any application which intends 
  372. to use the OUT register for its own purposes to modify the ORshadow 
  373. along with the OUT register.  By doing this, the interrupt handler is 
  374. provided with a readable copy of the contents of the OUT register so 
  375. that if an interrupt should occur while the application is using the 
  376. OUT register, the interrupt handler will restore the OUT register to 
  377.  
  378.  
  379.                                                                 Page 8 
  380.  
  381.  
  382. its correct contents before returning control to the application. 
  383.  
  384. The KBdisable flag is a single nibble which applications can write 
  385. with a nonzero value to indicate to the interrupt handler that the 
  386. keyboard is currently being scanned by an application.  When this 
  387. nibble is nonzero, the interrupt handler will not run the keyboard 
  388. service routine.  This is very useful for applications which have 
  389. their own keyboard input routines as it allows the programmer to 
  390. prevent the high CPU utilization by the interrupt handler as discussed 
  391. above.  The programmer should be aware, however, that the annunciator 
  392. flags will still be updated when the corresponding keys are pressed, 
  393. unless interrupts are disabled altogether as described below. 
  394.  
  395.  
  396.  
  397. 3.4.4  Utilizing The Built-in Data Structures 
  398.  
  399. Using the KeyBuf and KeyState data structures for your keyboard input 
  400. is very straightforward.  The routine provided in Appendix A shows how 
  401. to access the KeyBuf, and accessing the KeyState is trivial.  An 
  402. application need only examine the appropriate bits in KeyState to 
  403. determine if any given keys are being held down.  For applications 
  404. which can tolerate the additional CPU load of the interrupt handler, 
  405. it probably makes sense to just allow the normal interrupt mechanism 
  406. to service the keyboard, and then for the application to utilize the 
  407. KeyState and KeyBuf data structures.  It is generally only for 
  408. applications which perform CPU intensive or time-critical operations 
  409. that manual keyboard scanning techniques are required.  The following 
  410. sections will discuss this issue further, as well as how to write your 
  411. own keyboard input routines. 
  412.  
  413.  
  414.  
  415. 4  CUSTOM KEYBOARD I/O 
  416.  
  417. The main motivation for writing your own keyboard input routines is to 
  418. steal back valuable CPU time from the interrupt system for 
  419. applications that need it.  In order to recover the CPU time that the 
  420. HP48 normally uses up in the keyboard interrupt service routine, you 
  421. will need to write your own keyboard I/O routines, or you can just 
  422. cut/paste the routines given later in this document. 
  423.  
  424.  
  425.  
  426. 4.1  Controlling The HP48 Interrupt System 
  427.  
  428. Writing your own keyboard I/O routine does little good unless one also 
  429. disables the normal keyboard interrupt mechanism.  Fortunately, there 
  430. are a few options open to the machine language programmer in this 
  431. regard.  Described below are some general approaches to disabling 
  432. normal keyboard servicing. 
  433.  
  434.  
  435.                                                                 Page 9 
  436.  
  437.  
  438. 4.1.1  The Big Hammer 
  439.  
  440. The first method, which we will call "The Big Hammer", is to clear bit 
  441. 15 of the status register.  This bit is checked at the top of the 
  442. interrupt handler, and if it is clear then the interrupt handler 
  443. disables further interrupts and returns to the interrupted program. 
  444. Thus, clearing ST<15> effectively shuts off all I/O on the calculator, 
  445. namely the keyboard.  The interrupt system also sets ST<14> to 
  446. indicate that an interrupt request has been posted but was not 
  447. serviced. 
  448.  
  449. One disadvantage to the "big hammer" approach are that once you clear 
  450. ST<15>, if a code bug causes your program to "hang" with ST<15>=0, you 
  451. have no control over the calculator.  You can't turn it off; you can't 
  452. do [ON]-[C]; you can't do [ON]-[A][F].  The only thing you _can_ do is 
  453. to pull off the rubber foot hiding the reset button and jam a paper 
  454. clip in the hole.  Not much of a way to quit your application. 
  455.  
  456. Another problem is that preventing certain interrupts from being 
  457. serviced for extended periods of time can lead to problems in the 
  458. calculator.  Since the interrupt handler does nothing if ST<15> is 
  459. clear, none of the possible interrupt sources in the HP48 will be 
  460. serviced.  This includes, among other things, the low battery detect 
  461. circuitry.  Hence if a machine language program which has cleared 
  462. ST<15> is left running, it can drain the batteries completely, 
  463. resulting in total loss of memory.  Normally the low-battery detect 
  464. circuitry would interrupt the CPU, allowing the interrupt handler to 
  465. safely shut the system down into a very low power consumption state, 
  466. thus preserving RAM.  With ST<15> clear, however, this safety net is 
  467. removed.  For programs which run for durations of less than several 
  468. hours, however, this should not be a problem. 
  469.  
  470. Other sources of interrupts such as timer rollover and serial I/O 
  471. activity will be totally ignored as long as ST<15> is clear.  However, 
  472. the lack of interrupt-driven serial I/O capabilities may not be an 
  473. issue for many applications, and interrupt requests due to rollover of 
  474. the 32-bit hardware timer (TIMER2) can be ignored for 72 hours without 
  475. effecting the calculator's sense of time. 
  476.  
  477. An example of when clearing ST<15> can be particularly useful is when 
  478. the keyboard is actually being scanned.  In this way, software can 
  479. avoid the need to shadow the OUT register in ORshadow.  This works 
  480. because no interrupts are possible while ST<15>=0.  The application 
  481. should then set ST<15>=1 after completing the keyboard scan to allow 
  482. other interrupts, if desired.  See the ENABLE_INTR routine in Appendix 
  483. B for more information on how to re-enable interrupts after disabling 
  484. them via ST<15>. 
  485.  
  486.  
  487.  
  488.  
  489. 4.1.2  The Little Hammer 
  490.  
  491. Another approach to disabling the keyboard interrupts is to shut off 
  492. keyboard scanning at the source by executing an INTOFF instruction. 
  493.  
  494.  
  495.                                                                Page 10 
  496.  
  497.  
  498. This disables the automatic 1ms keyboard keyboard scan described 
  499. above.  As a result, interrupts no longer occur due to key presses, 
  500. with the exception of [ON] which always causes an interrupt. 
  501. Furthermore, since we have not disabled I/O altogether such as is the 
  502. case when ST<15> is cleared, we can still abort our ML application by 
  503. doing [ON]-[C] if the need arises. 
  504.  
  505. The only minor pitfall to this technique is that the INTOFF 
  506. instruction prevents _only_ the keyboard interrupts.  If one of the 
  507. other devices in the system causes an interrupt, then the interrupt 
  508. handler will still execute.  As it turns out, the vast majority of the 
  509. time spent in the interrupt handler is due to the keyboard service 
  510. routine.  Therefore, aside from the keyboard service routine in the 
  511. interrupt handler, allowing interrupts does not cost very much in 
  512. terms of CPU time. 
  513.  
  514. Fortunately, the keyboard service routine can be completely disabled 
  515. by writing the KBdisable nibble to a nonzero value, so that if an 
  516. interrupt should occur, the keyboard service routine will not be 
  517. executed.  The programmer should note that the [alpha], [leftshift], 
  518. and [rightshift] keys are polled outside the keyboard service routine 
  519. and the corresponding display annunciators updated.  Unfortunately, 
  520. there is no way to prevent this from happening aside from turning off 
  521. interrupts altogether via ST<15>.  This is generally not necessary, 
  522. however, since in applications which do not use serial I/O, interrupts 
  523. should not occur. 
  524.  
  525. The programmer should be aware that the interrupt service routines for 
  526. the serial I/O contain INTON instructions, so if serial I/O is used, 
  527. then INTOFF instruction will need to be repeatedly executed to prevent 
  528. keyboard interrupts from occurring. 
  529.  
  530.  
  531.  
  532. 4.1.3  Recommendations 
  533.  
  534. Because of the different features built into the HP48 interrupt 
  535. service routine, there are several approaches that a ML programmer can 
  536. take to disable normal keyboard operations.  While each technique has 
  537. its own strengths and weaknesses, there are some general programming 
  538. practices that can lead to "cleaner" solutions.  Below are a few hints 
  539. to keep in mind with respect to programming for custom keyboard input. 
  540.  
  541.      1.  Use INTOFF as a general technique to disabling keyboard 
  542.          interrupts, with critical "uninterruptable" sections of code 
  543.          protected by clearing ST<15>.  This generally works well, 
  544.          although there are a few things to keep in mind. 
  545.  
  546.           -  After re-enabling interrupts by setting ST<15>=1, you 
  547.              need to check ST<14> to determine whether an interrupt 
  548.              was requested while interrupts were disabled.  ST<14>=1 
  549.              means an interrupt was requested but has not yet been 
  550.              serviced.  Basically what happened is that an interrupt 
  551.              occurred, but since ST<15> was clear the interrupt 
  552.              handler immediately terminated via a RET instruction 
  553.  
  554.  
  555.                                                                Page 11 
  556.  
  557.  
  558.              rather than servicing the interrupt and terminating with 
  559.              an RETI instruction, as it does normally when ST<15>=1. 
  560.              The interrupt handler sets ST<14>=1 to inform the 
  561.              application that an interrupt is "pending". 
  562.  
  563.              The implication of this is that the HP48 believes that it 
  564.              is still executing in the interrupt handler (since no 
  565.              RETI has been executed since the last interrupt) and will 
  566.              not allow further interrupts until an RETI instruction is 
  567.              executed.  See the ENABLE_INTR routine in Appendix B for 
  568.              an example of how to handle this. 
  569.  
  570.           -  The Ticking Clock display or any user alarms which come 
  571.              due will be serviced whenever interrupts are enabled. 
  572.              This can lead to the INTON instruction being executed in 
  573.              the interrupt handler.  Once this occurs, then it is 
  574.              possible for keystrokes to cause interrupts. 
  575.  
  576.           -  If the Keyboard interrupt service routine has been 
  577.              disabled via the KBdisable flag, then keypresses should 
  578.              not make it into the KeyBuf, even if interrupts occur. 
  579.              If the application does not modify the KBdisable flag, 
  580.              then the KeyBuf may need to be periodically flushed to 
  581.              remove any keypresses that sneak into the KeyBuf. 
  582.  
  583.           -  Because any interrupts may lead to the execution of the 
  584.              INTON instruction, the application should periodically 
  585.              execute an INTOFF instruction. 
  586.  
  587.  
  588.      2.  Use "ST<15>=0" sparingly.  Try to keep sections of code which 
  589.          are protected from interrupts via the clearing of ST<15> as 
  590.          small as possible, and try to keep the "CLRB 15, ST" and 
  591.          "SETB 15, ST" instructions as local to each other as 
  592.          possible.  Remember, if you make a mistake and leave 
  593.          ST<15>=0, all keyboard control is lost.  Try to keep 
  594.          "uninterruptable" sections of code as small as possible, such 
  595.          as in the code example of Appendix B when the OUT register is 
  596.          being modified. 
  597.  
  598.  
  599.  
  600.  
  601. 4.2  Example Keyboard Input Routines 
  602.  
  603. The code example in Appendix B shows how custom keyboard I/O routines 
  604. can be written.  The code shown in this example is actually the 
  605. keyboard scanner process which runs in the game program "Vaders". 
  606. Vaders was written using MPE, a machine language multiprogramming 
  607. environment for the HP48.  The keyboard scanner process in Appendix B 
  608. runs concurrently with the other processes which make up the Vaders 
  609. game, making the keyboard scanning transparent to the other processes 
  610. which comprise the game. 
  611.  
  612. The example code maintains two data structures which reflect the 
  613.  
  614.  
  615.                                                                Page 12 
  616.  
  617.  
  618. status of the keyboard in a manner similar to the "KeyBuf" and 
  619. "KeyState" data structures maintained by the HP48's interrupt system. 
  620.  
  621.  
  622.  
  623.  
  624.  
  625.  
  626.  
  627.  
  628.  
  629.  
  630.  
  631.  
  632.  
  633.                               APPENDIX A 
  634.  
  635.                          KEYBUF CODE EXAMPLE 
  636.  
  637.  
  638.  
  639. ;;+ 
  640. ;; 
  641. ;; Keyboard Interface. 
  642. ;; 
  643. ;; Keyboard scan codes are numbered 1 for [A], 2 for [B], 3 [C]... 
  644. ;; #19h [ENTER]... #1Fh [7]... #31h [+]. [alpha] is #80h, [yellow] is 
  645. ;; #40h, and [blue] is #C0h. ON has no scan code. 
  646. ;;  
  647. ;; This program reads the keyboard buffer. If a key is present, it 
  648. ;; returns it in A.A. If no key is present, it enters light sleep and 
  649. ;; waits for one. 
  650. ;; 
  651. ;; kb_poll polls the keyboard buffer, carry set if non-empty, key in A.A. 
  652. ;; kb_get does the same, but waits until a key is pressed. 
  653. ;; 
  654. ;; Jan Brittenson, April 1991 
  655. ;; This program is in the Public Domain 
  656. ;; 
  657. ;;- 
  658.  
  659.         radix   ^d16 
  660.  
  661. event_mask = 10e 
  662.  
  663. ;; Poll keyboard buffer 
  664.  
  665. kb_poll: 
  666.         move.5  keybuf+1, d0    ; KB Put ptr 
  667.         move.s  @d0, a          ; A.S = put ctr 
  668.         dec     d0 
  669.         move.s  @d0, c          ; C.S = get ctr 
  670.         breq.s  c, a, $100      ; Ctrs are equal - buffer empty 
  671.  
  672.         move    c.15, p         ; P = get ctr 
  673.         inc.s   c               ; Remove key 
  674.         move.s  c, @d0 
  675.  
  676.         swap    c, d0 
  677.         add     p+1, c 
  678.         add     p+1, c          ; C += get ctr, in bytes 
  679.  
  680.  
  681. KEYBUF CODE EXAMPLE                                           Page A-2 
  682.  
  683.  
  684.         clr     p 
  685.         move    c, d0           ; D0 = &next key 
  686.         clr.a   a 
  687.         move.b  @d0, a          ; A.A = key 
  688.         retsetc 
  689. $100: 
  690.         clr.a   a 
  691.         retclrc 
  692.  
  693. ;; Wait for a key to become pressed, then return scan code in 
  694. ;; A.B. Uses C.A and B.B. 
  695.  
  696. kb_get: 
  697.         call    kb_poll         ; Get key, if any 
  698.         retcs                   ; Return if there was a key in the buffer 
  699.  
  700. ; No keys are down - enter light sleep 
  701.  
  702.         move.5  event_mask, d0 
  703.         move.p1 8, c 
  704.         move.1  c, @d0 
  705.         rsi 
  706.         shutdn                  ; Go asleep 
  707.         move.p1 0xc, c          ; Restore event mask 
  708.         move.1  c, @d0 
  709.         jump    kb_get          ; Check buffer again 
  710.  
  711.  
  712.  
  713.  
  714.  
  715.  
  716.  
  717.  
  718.  
  719.  
  720.  
  721.  
  722.  
  723.                               APPENDIX B 
  724.  
  725.                     CUSTOM KEYBOARD I/O ROUTINES. 
  726.  
  727.  
  728.  
  729.  
  730. ;********************************************************************* 
  731. ;********************************************************************* 
  732. ; This process is responsible for scanning the keyboard and updating  
  733. ; the KEY_SCAN and NEW_KEYS data structures.  The routines GET_KEYS  
  734. ; and GET_NEW_KEYS are used to check these two data structures,  
  735. ; respectively, for key presses. 
  736.  
  737.         keybuf          =       ^x704EA ; keyboard buffer. 
  738.         DO_IN_4         =       ^x1160  ; Does IN.4 C. 
  739.  
  740. KEY_SCAN:       DATA.w  0       ; This data structure holds a shadow  
  741.                                 ; of the status of the keyboard.  Each  
  742.                                 ; bit represents a key, although some  
  743.                                 ; bits are unused. 
  744.  
  745. NEW_KEYS:       DATA.w  0       ; This word indicates whether any keys  
  746.                                 ; have been newly pressed.  Each bit   
  747.                                 ; represents one key, although some  
  748.                                 ; bits are unused. 
  749.  
  750.  
  751. GET_KEYS:       ; This routine just looks in the key_scan data for   
  752.                 ; the presense of any of the keys specified in the  
  753.                 ; key mask in C.w. 
  754.  
  755.         addr    key_scan, d0    ; Address of key_scan data in D0. 
  756.         move.w  @d0, a          ; Get key status. 
  757.         and.w   a, c            ; Allow only the selected keys. 
  758.         ret  
  759.  
  760.  
  761. GET_NEW_KEYS:   ; This routine checks the new_key data for new key  
  762.                 ; presses. The calling procedure must supply a key   
  763.                 ; mask in C.w.  Any new keys selected by the mask   
  764.                 ; are cleared from the new_key data.  A nonzero value  
  765.                 ; is returned in C.w representing which of the selected  
  766.                 ; keys were pressed. 
  767.  
  768.         addr    new_keys, d0    ; Trashes A. 
  769.  
  770.  
  771. CUSTOM KEYBOARD I/O ROUTINES.                                 Page B-2 
  772.  
  773.  
  774.         move.w  @d0, a          ; Get new_keys. 
  775.         and.w   a, c            ; Allow only selected keys. 
  776.         not.w   c               ; Invert result and apply to A 
  777.         and.w   c, a            ; to clear selected keys from new_keys. 
  778.         move.w  a, @d0          ; Write updated new_keys data. 
  779.         not.w   c               ; Invert result and apply to A 
  780.         ret 
  781.  
  782. ENABLE_INTR:                    ; This routine turns interrupt servicing back  
  783.                                 ; on after an interrupts were disabled by 
  784.                                 ; clearing ST<15>.  This routine checks for 
  785.                                 ; pending interrupts and ensures that they are 
  786.                                 ; serviced. 
  787.         setb    ^d15, st        ; Reenable IO interrupt service.  We want to  
  788.                                 ; do this so that any important interrupts in  
  789.                                 ; the system will still be recognized. 
  790.         brbc    ^d14, st, $1    ; If ST<14> is set, then we have missed an 
  791.                                 ; interrupt while ST<15> was clear, so we  
  792.                                 ; need to re-enable interrupts. 
  793.         clrb    ^d14, st        ; Clear the pending interrupt flag. 
  794.         RSI                     ; Reset the keyboard interrupt state machine. 
  795. $1:     RETI                    ; Re-enable interrupt servicing.  If there is 
  796.                                 ; an pending interrupt, it will be serviced 
  797.                                 ; now.   
  798.  
  799. PROCESS8_INIT: 
  800.  
  801. ; R0:   Holds current key status. 
  802. ; R1:   Holds the old key status. 
  803. ; R2:    
  804. ; R3: 
  805. ; R4: 
  806.  
  807.  
  808.  
  809.  
  810.         cur_process_start process8_code 
  811.  
  812.  
  813.         addr    new_keys, d1    ; D1 holds pointer to new_keys data. 
  814.         addr    key_scan, d0    ; Get address of the key scan bit pattern. 
  815.         clr.w   a 
  816.         move.w  a, r0 
  817.         move.w  a, r1           ; Initialize current and old key status. 
  818.         move.w  a, @d0          ; Clear the keyboard scan pattern. 
  819.         move.w  a, @d1          ; Clear new_keys. 
  820.  
  821. PROCESS8_CODE: 
  822.         intoff                  ; Shut off the system 1ms keyboard scan, 
  823. again. 
  824.                                 ; This is necessary because 
  825.                                 ; if other interrupts have occurred, then the 
  826.                                 ; INTON instruction may have been executed in 
  827.                                 ; the interrupt handler, re-enabling keyboard 
  828.                                 ; interrupts. 
  829.  
  830.  
  831.  
  832. CUSTOM KEYBOARD I/O ROUTINES.                                 Page B-3 
  833.  
  834.  
  835.         move.5  keybuf+1, d0    ; Point at "put" pointer. 
  836.         move.1  @d0, c          ; Get the "put" pointer. 
  837.         dec     d0              ; Point at "get" pointer. 
  838.         move.1  c, @d0          ; Flush the keybuf just in case any characters 
  839.                                 ; have snuck into the keybuffer.         
  840.         addr    key_scan, d0    ; Get address of the key scan bit pattern. 
  841.  
  842. ; First we just want to check to see if any keys are being pressed. 
  843.         clrb    ^d15, st        ; Shut off IO interrupt service for now.  This 
  844.                                 ; needs to be done here so that any interrupts 
  845.                                 ; generated in the process of scanning the  
  846.                                 ; keyboard will be ignored by the system.   
  847.                                 ; Keyboard interrupts are possible because 
  848.                                 ; if other interrupts have occurred, then the 
  849.                                 ; INTON instruction may have been executed in 
  850.                                 ; the interrupt handler, re-enabling keyboard 
  851.                                 ; interrupts. 
  852.  
  853.         clr.w   c 
  854.         move.p3 ^x1FF, c        ; OUT value for entire keyboard. 
  855.         out.x   c 
  856.         call.a  do_in_4 
  857.         clr     p 
  858.         brnz.w  c, $1           ; If no keys are pressed we just fall through. 
  859.         clr.w   c 
  860.         move.w  c, @d0          ; Zero out the key_scan scoreboard. 
  861.         move.w  r0, a           ; Old "current" key status. 
  862.         move.w  a, r1           ; R1 holds the old key status. 
  863.         move.w  c, r0           ; R0 holds the current key status. 
  864.         jump.4  process8_sched  ; Go reschedule the process. 
  865.  
  866. $1:     ; Now we need to scan the keyboard and build up the key scoreboard.    
  867.  
  868.  
  869.         addr    new_keys, d1    ; D1 holds pointer to new_keys data. 
  870.         clr.w   a               ; Temp storage for scanned keys. 
  871.         clr.w   c 
  872.         move.p3 ^x100, c 
  873.         move.w  c, d            ; Leave a copy in D, where it will be shifted. 
  874. $3:     move.w  d, c            ; Put scan pattern into C. 
  875.         out.x   c               ; scan the next row. 
  876.         call.a  do_in_4         ; Read the keys. 
  877.         add.w   a, a            ; Shift the data in A 6 bits to the left. 
  878.         add.w   a, a 
  879.         add.w   a, a 
  880.         add.w   a, a 
  881.         add.w   a, a 
  882.         add.w   a, a 
  883.         OR.w    C, A            ; Add in the new keys to the data in A. 
  884.         srb.w   d               ; Shift to look at next keyboard row. 
  885.         brnz.w  d, $3           ; Do another row if not done yet. 
  886.  
  887.         move.w  a, @d0          ; Write out the scanned keyboard data. 
  888.  
  889.         move.w  r0, c           ; Old "current" key status. 
  890.         move.w  c, r1           ; R1 holds the old key status. 
  891.         move.w  a, r0           ; R0 holds the current key status. 
  892.  
  893.  
  894. CUSTOM KEYBOARD I/O ROUTINES.                                 Page B-4 
  895.  
  896.  
  897.         not.w   c               ; Invert the old key status... 
  898.         and.w   a, c            ; and AND in the new keys.  The resulting word 
  899.                                 ; in C indicates the "new" key presses. 
  900.         move.w  @d1, a          ; Get existing value of NEW_KEYS data. 
  901.         or.w    a, c            ; Add in the new keys without destroying any 
  902.                                 ; "new" keys that haven't been serviced yet. 
  903.         move.w  c, @d1          ; D1 points to New_keys data. 
  904.  
  905.  
  906.         clr.w   c 
  907.         move.p3 ^x1FF, c        ; OUT value for entire keyboard. 
  908.         out.x   c               ; Just to reset things so when we exit to 
  909.                                 ; the RPL environment the system can still 
  910.                                 ; read the keyboard.  This is needed because 
  911.                                 ; the 1ms keyboard does not reload the OUT  
  912.                                 ; register. 
  913.  
  914.  
  915.  
  916. PROCESS8_SCHED: 
  917.  
  918.  
  919.         call.4  save_context    ; Save the process context. 
  920.         call.4  enable_intr     ; Re-enables I/O by setting ST<15>=1 and 
  921.                                 ; executes an RETI instruction to allow  
  922.                                 ; interrupt servicing.   
  923.         CLR.W   c 
  924.         MOVE.P3 ^xFF, c         ; Reschedule this process to look at the 
  925.                                 ; keyboard roughly 30 times per second. 
  926.         call.4  resch_cur       ; Reschedule the current process. 
  927.  
  928.  
  929. PROCESS8_EXIT: 
  930.         JUMP.4  TO_SCHEDULER    ; Return control to the scheduler. 
  931.  
  932. ; Thus ends process #8. 
  933.